#pragma once

#include "sock.hpp"

#include <poll.h>
#include <sys/types.h>

namespace ns_poll
{
    //文件描述符最多被接收的数量NUM（也可以扩容）
    #define NUM 1024

    using namespace ns_sock;
    const uint16_t g_default = 8080;

    class PollServer
    {
    public:
        PollServer(u_int16_t port = g_default) : port_(port), listen_sock_(-1)
        {
            for(int i = 0; i < NUM; i++)
            {
                pollfds_[i].fd = -1;
                pollfds_[i].events = 0;
                pollfds_[i].revents = 0;
            }
        }

        void InitPollServer()
        {
            listen_sock_ = Sock::Socket();
            Sock::Bind(listen_sock_, port_);
            Sock::Listen(listen_sock_);
            pollfds_[0].fd = listen_sock_;
            pollfds_[0].events = POLLIN;    //表示我关心读事件
        }

        //对新链接的到来 或者 新数据 的处理
        void HandlerEvent(struct pollfd pollfds[])
        {
            //判断我的有效sock，是否在pollfds中
            for(int i = 0; i < NUM; i++)
            {
                if(pollfds[i].fd == -1)
                    continue;
                //如何区分: 新链接到来，真正的数据到来？
                if(pollfds[i].revents & POLLIN)
                {
                    if(pollfds[i].fd == listen_sock_)
                    {
                        //新链接到来
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);
                        int sock = accept(listen_sock_, (struct sockaddr*)&peer, &len);
                        if(sock < 0)
                        {
                            std::cout << "accept error" << std::endl;
                        }
                        else 
                        {
                            //不能直接读取新的sock，为什么要将它添加到数组就完了？？
                            //将新的sock添加到文件描述符数组中！
                            int j = 0;
                            for(; j < NUM; j++)
                            {
                                if(pollfds[j].fd == -1)
                                    break;
                            }
                            if(j == NUM)
                            {
                                //可以扩容，这里不做处理
                                close(sock);
                            }
                            else 
                            {
                                std::cout << "get a new sock : " << sock << std::endl;
                                pollfds[j].fd = sock;
                                pollfds[j].events = POLLIN;
                                pollfds[j].revents = 0;                      
                            }
                        }
                    }
                    else 
                    {
                        // 数据到来
                        // 这样写是有BUG的！这里不解决，epoll
                        // 你能保证你用1024就能读取完毕吗？？有没有可能有粘包问题？？
                        // 网络通信，定制协议，业务场景有关
                        // 是不是每一个sock，都必须有自己独立的buffer
                        char buffer[1024];
                        ssize_t s = recv(pollfds[i].fd, buffer, sizeof(buffer), 0);
                        if( s > 0 )
                        {
                            buffer[s] = '\0';
                            std::cout << "clint say# " << buffer << std::endl;
                        }
                        else if(s == 0)
                        {
                            std::cout << "client quit ---- sock: " << pollfds[i].fd << std::endl;
                            // 对端链接关闭
                            close(pollfds[i].fd);
                            pollfds[i].fd = -1;
                            pollfds[i].events = 0;
                            pollfds[i].revents = 0;
                        }
                        else
                        {
                            //读取异常，TODO
                            std::cerr << "recv error" << std::endl;
                        }
                    }
                }
            }
        }

        void Loop()
        {
            //在服务器最开始的时候，我们只有一个sock，listen_sock, 有读事件就绪，读文件描述符看待的！
            int timeout = -1;   //阻塞
            while(true)
            {
                int n = poll(pollfds_, NUM, timeout);
                switch (n)
                {
                case 0:
                    std::cout << "timeout ..." << std::endl;
                    break;
                case -1:
                    std::cout << "poll error" << std::endl;
                    break;
                default:
                    HandlerEvent(pollfds_);
                    break;
                }
            }
        }

        ~PollServer()
        {
            if (listen_sock_ >= 0)
                close(listen_sock_);
        }

    private:
        u_int16_t port_;
        int listen_sock_;
        struct pollfd pollfds_[NUM];    //用于输入和输出文件描述符位图结构的结构体数组
    };
}